package com.resort.trade.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.disney.domain.sdk.common.CommonConstants;
import com.disney.domain.sdk.common.DisneyResult;
import com.disney.domain.sdk.exception.BizException;
import com.disney.domain.sdk.enums.DomainAbsBizType;
import com.disney.domain.sdk.enums.DomainBizType;
import com.disney.domain.sdk.factory.DomainExecutor;
import com.disney.domain.sdk.factory.DomainRequest;
import com.disney.domain.sdk.mq.DomainMessage;
import com.disney.domain.sdk.publish.DomainPublisher;
import com.resort.payment.api.WapPayService;
import com.resort.payment.api.request.TradeCreateRequest;
import com.resort.payment.api.response.TradeCreateResponse;
import com.resort.trade.api.order.TradeService;
import com.resort.trade.api.order.request.*;
import com.resort.trade.api.order.response.OrderCancelDTO;
import com.resort.trade.api.order.response.OrderCreateDTO;
import com.resort.trade.api.order.response.OrderPaymentDTO;
import com.resort.trade.api.order.response.OrderValidateDTO;
import com.resort.trade.domain.even.domain.CancelOrderEven;
import com.resort.trade.domain.even.domain.PayCallBackEven;
import com.resort.trade.domain.even.domain.PaymentEven;
import com.resort.trade.domain.even.mq.PayTimeOutEven;
import com.resort.trade.domain.gateway.FundOrderGateway;
import com.resort.trade.domain.gateway.OrderGateway;
import com.resort.trade.domain.gateway.OrderItemGateway;
import com.resort.trade.domain.gateway.OrderItemSkuGateway;
import com.resort.trade.domain.model.FundOrder;
import com.resort.trade.domain.model.Order;
import com.resort.trade.domain.model.OrderItem;
import com.resort.trade.apps.*;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import com.resort.trade.service.convert.OrderRequestConvert;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Optional;

/**
 * @author issavior
 */
@DubboService
public class TradeServiceImpl implements TradeService {

    @Resource
    private OrderGateway orderGateway;
    @Resource
    private OrderItemGateway orderItemGateway;
    @Resource
    private OrderItemSkuGateway orderItemSkuGateway;
    @Resource
    private FundOrderGateway fundOrderGateway;
    @Resource
    private RocketMQTemplate rocketMQTemplate;

    @DubboReference
    private WapPayService wapPayService;

    private static final DomainExecutor<OrderValidateDomainBiz> VALIDATE_EXECUTOR = new DomainExecutor<>(OrderValidateDomainBiz.class);
    private static final DomainExecutor<OrderCreateDomainBiz> CREATE_EXECUTOR = new DomainExecutor<>(OrderCreateDomainBiz.class);
    private static final DomainExecutor<OrderTocDomainBiz> TOC_EXECUTOR = new DomainExecutor<>(OrderTocDomainBiz.class);
    private static final DomainExecutor<OrderPaymentDomainBiz> PAY_EXECUTOR = new DomainExecutor<>(OrderPaymentDomainBiz.class);
    private static final DomainExecutor<OrderCancelDomainBiz> CANCEL_EXECUTOR = new DomainExecutor<>(OrderCancelDomainBiz.class);


    @Override
    public DisneyResult<OrderValidateDTO> orderValidate(OrderValidateRequest request) {

        DomainRequest domainRequest = DomainRequest.to()
                .code(DomainBizType.of(request.getBizCode()))
                .scenario(DomainAbsBizType.TRADE)
                .end();
        OrderRequest orderRequest = VALIDATE_EXECUTOR.execFirst(domainRequest, biz -> biz.buildOrderValidateRequest(request));
        VALIDATE_EXECUTOR.execFirst(domainRequest, biz -> biz.orderValidateRequestCheck(orderRequest));
        VALIDATE_EXECUTOR.execFirst(domainRequest, biz -> biz.checkStock(orderRequest));
        return DisneyResult.ok();
    }

    @Override
    public DisneyResult<OrderCreateDTO> orderCreate(OrderCreateRequest request) {

        OrderValidateRequest orderValidateRequest = OrderRequestConvert.toOrderValidateRequest(request);
        DisneyResult<OrderValidateDTO> orderValidate = orderValidate(orderValidateRequest);
        if (orderValidate.getCode() != CommonConstants.SUCCESS) {
            throw new BizException("价库出现异常，请重新选择商品");
        }

        DomainRequest domainRequest = DomainRequest.to()
                .code(DomainBizType.of(request.getBizCode()))
                .scenario(DomainAbsBizType.TRADE)
                .end();
        OrderRequest orderRequest = CREATE_EXECUTOR.execFirst(domainRequest, biz -> biz.buildOrderCreateRequest(request));
        CREATE_EXECUTOR.execFirst(domainRequest, biz -> biz.checkOrder(orderRequest));
        Order order = CREATE_EXECUTOR.execFirst(domainRequest, biz -> biz.buildOrder(orderRequest));
        orderGateway.createOrder(order);
        OrderItem orderItem = CREATE_EXECUTOR.execFirst(domainRequest, biz -> biz.buildOrderItem(request.getGoodsSubSubDTO()));
        orderItemGateway.createOrderItem(orderItem);
        request.getGoodsSubSubDTO().getGoodsSubSubSubS()
                .forEach(goodsSubSubSubDTO -> orderItemSkuGateway
                        .createOrderItemSku(CREATE_EXECUTOR.execFirst(domainRequest,
                                biz -> biz.buildOrderItemSku(goodsSubSubSubDTO))));
        CREATE_EXECUTOR.execFirst(domainRequest, biz -> biz.minusStock(orderRequest));
        // todo 锁券

        Integer integer = TOC_EXECUTOR.execFirst(domainRequest, OrderTocDomainBiz::orderPayExpireMin);
        // todo 开源RocketMQ的延时是按等级划分 待优化
        // 参数一：topic   如果想添加tag,可以使用"topic:tag"的写法
        // 参数二：Message<?>
        // 参数三：消息发送超时时间
        // 参数四：delayLevel 延时level  messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
        DomainPublisher.publish(new PayTimeOutEven<Order>(DomainMessage.<Order>builder().message(order).build()));
        return DisneyResult.ok();
    }

    @Override
    public DisneyResult<OrderPaymentDTO> orderPayment(OrderPaymentRequest request) {

        Order order = orderGateway.findById(request.getOrderId());

        if (order.isEnd()) {
            throw new BizException("订单异常，请联系客服");
        }
        FundOrder fundOrder = fundOrderGateway.findByOrderId(request.getOrderId());
        Optional.ofNullable(fundOrder).ifPresent(FundOrder::idempotent);

        DomainRequest domainRequest = DomainRequest.to()
                .code(DomainBizType.of(request.getBizCode()))
                .scenario(DomainAbsBizType.TRADE)
                .end();
        // todo 调支付服务的支付接口，获取交易号
        DisneyResult<TradeCreateResponse> result = wapPayService.getTradeNo(TradeCreateRequest.builder().build());
        if (!result.isSuccess()) {
            return DisneyResult.failed("交易失败，请重新下单");
        }

        fundOrder = PAY_EXECUTOR.execFirst(domainRequest, biz -> biz.buildFundOrder(order, "tradeNo"));
        DomainPublisher.publish(PaymentEven.builder().order(order).fundOrder(fundOrder).build());
        orderGateway.updateStatuses(order);
        fundOrderGateway.insert(fundOrder);
        return DisneyResult.ok(OrderPaymentDTO.builder().build());
    }

    @Override
    public DisneyResult<Void> orderPayCallBack(Map<String, String> params) {

        // todo 调支付服务，check参数是否正确

        AlipayCallBackRequest alipayCallBackRequest = JSON.parseObject(JSON.toJSONString(params), AlipayCallBackRequest.class);
        Long orderId = Long.valueOf(alipayCallBackRequest.getOutTradeNo());
        Order order = orderGateway.findById(orderId);
        FundOrder fundOrder = fundOrderGateway.findByOrderId(orderId);

        if (fundOrder.isSuccessEnd()) {
            return DisneyResult.ok();
        }

        if (!"TRADE_SUCCESS".equals(alipayCallBackRequest.getTradeStatus())) {
            // todo 失败处理
            DomainPublisher.publish(PayCallBackEven.builder()
                    .success(false)
                    .order(order)
                    .fundOrder(fundOrder)
                    .build());
        } else {
            DomainPublisher.publish(PayCallBackEven.builder()
                    .success(true)
                    .order(order)
                    .fundOrder(fundOrder)
                    .build());
        }
        orderGateway.updateStatuses(order);
        fundOrderGateway.updateStatuses(fundOrder);

        return DisneyResult.ok();

    }


    @Override
    public DisneyResult<OrderCancelDTO> orderCancel(OrderCancelRequest request) {
        Long orderId = request.getOrderId();
        DomainRequest domainRequest = DomainRequest.to()
                .code(DomainBizType.of(request.getBizCode()))
                .scenario(DomainAbsBizType.TRADE)
                .end();
        CANCEL_EXECUTOR.execFirst(domainRequest, biz -> biz.fundHandler(fundOrderGateway.findByOrderId(orderId), "dubbo"));

        Order order = orderGateway.findById(orderId);
        DomainPublisher.publish(CancelOrderEven.builder().order(order).build());

        return DisneyResult.ok();

    }
}